#include <string>
#include <functional>
#include <mutex>
#include <fstream>
#include <memory>
#include <algorithm>

#include "misc.hpp"
#include "url.hpp"
#include "debug.hpp"
#include "details/httpFile.hpp"

using namespace wheels;

const long curlHttp :: DEFAULT_BUFFER_SIZE = 1024 * 1024 * 5; // 默认5M的下载内存缓冲
const int curlHttp :: DEFAULT_THD_COUNT = 4;
const long curlHttp :: DEFAULT_OVERTIME = -1;                // http通讯超时时间

struct stParams
{
    curlHttp                         * p_obj;
    std::shared_ptr< std::ofstream >   pt_ofs;
};

size_t curlHttp_write_callback(char *ptr, size_t size, size_t nmemb, void *obj )
{
    if( obj == nullptr ) return CURLE_OK;
    if( ptr == nullptr ) return CURLE_OK;

    stParams * item = reinterpret_cast< stParams*> ( obj );
    curlHttp * __obj = item->p_obj;

    size_t len = size * nmemb;

    __obj->on_recv__( ptr , len , item->pt_ofs );

    return len;
}

void curlHttp :: on_recv__( const char * data , size_t len ,std::shared_ptr< std::ofstream > pofs )
{
    if( data && len && pofs ){
        pofs->write( data , len );
        if( pofs->bad() ){
            ERROR_MSG( "写入文件失败" );
        }
    }
}

curlHttp :: curlHttp():
    m_buff_size__( DEFAULT_BUFFER_SIZE ),
    m_timer__( [this]{ run();} , 1000 ),
    m_overtime__( DEFAULT_OVERTIME )
{
	try{
        pt_thd_pool__ = std::make_shared< threadPool >( DEFAULT_THD_COUNT  , 100 );
        m_files__ = std::make_shared< files_t >();

        CURLcode err = curl_global_init(CURL_GLOBAL_ALL);
        if( err != CURLE_OK ){
            ERROR_MSG( curl_easy_strerror( err ));
            pt_thd_pool__->stop();
            throw std::runtime_error( "初始化CURL库失败" );
        }
	}catch( std::bad_alloc& e ){
        debug::printCallstack();
		ERROR_MSG( e.what());
	}
	
}
curlHttp :: ~curlHttp()
{
	if( pt_thd_pool__ ){
		pt_thd_pool__->stop();
	}

    curl_global_cleanup();
}

bool curlHttp :: download__( const std::string& from , const std::string& dest )
{
    bool ret = true;

    CURL * p_ctx = curl_easy_init();

    if( p_ctx == nullptr ){
        debug::printCallstack();
        ERROR_MSG( "初始化libcurl失败");
        return false;
    }

    URL url( from );
    std::string ptcl = url.Protocol();
    std::transform(ptcl.begin(), ptcl.end() , ptcl.begin() , ::tolower );

    stParams param;
    param.p_obj = this;
    try{
        param.pt_ofs = std::make_shared<std::ofstream >( dest , std::ios_base::binary );
    }catch( std::bad_alloc& e ){
        ERROR_MSG( e.what() );
        debug::printCallstack();
        curl_easy_cleanup( p_ctx );
        return false;
    }
    curl_easy_setopt( p_ctx , CURLOPT_WRITEDATA , this );

    curl_easy_setopt( p_ctx , CURLOPT_URL, from.c_str() );
    // 配置SSL
    if( ptcl == "https" ){
        curl_easy_setopt( p_ctx , CURLOPT_PROTOCOLS, CURLPROTO_HTTPS );        // 使用HTTPS
        curl_easy_setopt(p_ctx, CURLOPT_SSL_VERIFYPEER, false );
        curl_easy_setopt(p_ctx, CURLOPT_SSL_VERIFYHOST, false );
    }else if(ptcl == "http"){
        curl_easy_setopt( p_ctx , CURLOPT_PROTOCOLS, CURLPROTO_HTTP );         // 使用HTTP协议
    }else{
        ERROR_MSG( "不支持的协议" );
        debug::printCallstack();
        curl_easy_cleanup( p_ctx );
        return false;
    }
    // 配置HTTP头资料，接受所有的反馈格式
    struct curl_slist   * ptHeaders = NULL;
    ptHeaders = curl_slist_append( ptHeaders , "Accept:*/*" );
    curl_easy_setopt( p_ctx , CURLOPT_HTTPHEADER , ptHeaders );

    curl_easy_setopt( p_ctx , CURLOPT_WRITEFUNCTION , curlHttp_write_callback );   // 设置接收数据回调处理函数
    curl_easy_setopt( p_ctx , CURLOPT_HTTPGET , 1 );                           // 提及方式为POST
    // 要发送的数据
    curl_easy_setopt( p_ctx , CURLOPT_TIMEOUT , m_overtime__.load() );		   // 设置超时时间

    CURLcode rc = curl_easy_perform( p_ctx );
    if( rc != CURLE_OK ){
        ERROR_MSG( curl_easy_strerror( rc ));
        debug::printCallstack();
        if( ptHeaders ){
            curl_slist_free_all( ptHeaders );
        }
        curl_easy_cleanup( p_ctx );
        return false;
    }
    if( ptHeaders ){
        curl_slist_free_all( ptHeaders );
    }
    curl_easy_cleanup( p_ctx );
    if( param.pt_ofs->bad() ){
        ERROR_MSG( "下载文件写入失败" );
    }
    param.pt_ofs->close();
    return ret;
}

void curlHttp :: bufferSize( size_t s )
{
	m_buff_size__ = s;
}


bool curlHttp :: download( const std::string& from , const std::string& dest , bool autoRun )
{
	if( autoRun ){
		return download__( from , dest );
	}else{
		auto item = std::make_pair( from , dest );
		{
			std::lock_guard< std::mutex > lck( m_mutex__ );
            m_files__->push_back( std::move( item ) );
		}
	}

	return true;
}

void curlHttp :: download( const std::string& from , const std::string& dest ,
			   std::function< bool ( emErrCode , uint8_t * ,size_t  ) > cb , bool autoRun )
{
	if( ( !m_cb__ ) && cb ){
        m_cb__ = cb;
	}

	if( autoRun ){
		download__( from , dest );
	}else{
		auto item = std::make_pair( from , dest );
		{
			std::lock_guard< std::mutex > lck( m_mutex__ );
            m_files__->push_back( std::move( item ) );
		}
	}
}

void curlHttp :: download( const std::string& from , char * buffer ,
			   std::function< bool ( emErrCode , uint8_t * ,size_t ) > cb , bool autoRun )
{
	(void)from;
	(void)buffer;
	(void)cb;
	(void)autoRun;
	/*if( ( !m_cb__ ) && cb ){
		m_cb__ = cb
	}

	if( autoRun ){
		download__( from , buff );
	}else{
		auto item = std::make_pair( from , "" );
		{
			std::lock_guard< std::mutex > lck( m_mutex__ );
			pt_files__->push_back( std::move( item ) );
		}
    }*/
}

bool curlHttp :: download( const ArrayString& f , const ArrayString& d , bool autoRun )
{
	bool ret = true;
    for( size_t i = 0; i < f.size(); i ++ ){
		std::string from = f[ i ], dest = d[ i ];
		if( autoRun ){
			ret &= download__( from , dest );
		}else{
            auto item = std::make_pair( from , dest );
			{
				std::lock_guard< std::mutex > lck( m_mutex__ );
                m_files__->push_back( std::move( item ) );
			}
		}
	}

	return ret;
}

void curlHttp :: download( const ArrayString& f , const ArrayString& d ,
			   std::function< bool ( emErrCode , uint8_t * ,size_t ) > cb, bool autoRun )
{
	if( ( !m_cb__ ) && cb ){
        m_cb__ = cb;
	}

    for( size_t i = 0; i < f.size(); i ++ ){
		std::string from = f[ i ], dest = d[ i ];
		if( autoRun ){
			download__( from , dest );
		}else{
            auto item = std::make_pair( from , dest );
			{
				std::lock_guard< std::mutex > lck( m_mutex__ );
                m_files__->push_back( std::move( item ) );
			}
		}
	}
}

bool curlHttp :: upload( const std::string& file ,  const std::string& remote_file ,  bool autoRun )
{
	(void)file;
	(void)remote_file;
	(void)autoRun;
	return true;
}

void curlHttp :: upload( const std::string& file ,  const std::string& remote_file,
			 std::function< bool ( emErrCode , size_t )> cb ,bool autoRun )
{
	(void)file;
	(void)remote_file;
	(void)cb;
	(void)autoRun;
}

bool curlHttp :: upload( const ArrayString& from , bool autoRun )
{
	(void)from;
	(void)autoRun;
	return true;
}

void curlHttp :: upload( const ArrayString& file , std::function< bool ( emErrCode , size_t )> cb , bool autoRun )
{
	(void)file;
	(void)cb;
	(void)autoRun;
}

bool curlHttp :: del( const std::string& rem )
{
	(void)rem;
	return true;
}

void curlHttp :: setUser( const std::string& acc , const std::string& pswd )
{
	(void)acc;
	(void)pswd;
}

void curlHttp :: addUrl( const std::string& url )
{
	(void)url;
}

void curlHttp :: run()
{
	if( !pt_thd_pool__ ){
		ERROR_MSG( "线程池没有正确初始化" );
		return;
	}

	if( ( !m_files__ ) || m_files__->size() == 0 ){
		ERROR_MSG( "没有下载内容或者下载数据表无效" );
	}
	try{
		for(; m_files__->size() > 0; m_files__->pop_front() ){
            std::lock_guard< std::mutex > lck( m_mutex__ );
			auto item = m_files__->front();
			if( item.second.empty() == false ){
				pt_thd_pool__->enqueue(
                    std::bind( &curlHttp :: download__ , this , std::placeholders::_1, std::placeholders::_2 ), item.first , item.second );
			}		 
		}
	}catch( std::runtime_error& e ){
        m_timer__.Start( -1 , false );
	}
}

void curlHttp :: run( std::function< bool ( emErrCode , size_t , int , const std::string& ) > cb )
{
	if( cb ){
        m_cb1__ = cb;
	}

	run();
}

curlHttp :: emErrCode
curlHttp :: ls( ArrayString& rst )
{
	(void)rst;
	return OK;
}

void curlHttp :: ls( std::function< void ( emErrCode , ArrayString& ) > cb )
{
	(void) cb;
}

curlHttp :: emErrCode
curlHttp :: access( const std::string& url , int type )
{
	(void)url;
	(void)type;
	return OK;
}

curlHttp :: emErrCode
curlHttp :: cd( const std::string& path )
{
	(void)path;
	return OK;
}

size_t curlHttp :: size( const std::string& file )
{
	(void)file;
	return 0;
}
